package com.xugui.thumb.job;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xugui.thumb.mapper.BlogMapper;
import com.xugui.thumb.pojo.entity.Thumb;
import com.xugui.thumb.pojo.enums.ThumbTypeEnum;
import com.xugui.thumb.services.ThumbService;
import com.xugui.thumb.utils.RedisKeyUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 定时将 Redis 中的临时点赞数据同步到数据库
 */  
@Component
@Slf4j
@RequiredArgsConstructor
public class SyncThumb2DBJob {  

    private final ThumbService thumbService;

    private final BlogMapper blogMapper;

    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 如果任务数量很大超过10s则等待补偿机制
     */
    @Scheduled(fixedRate = 10000)  // 10s 中
    @Transactional(rollbackFor = Exception.class)
    public void run() {  
        log.info("开始执行");  
        DateTime nowDate = DateUtil.date();
        // 如果秒数为0 ~ 9 则回到上一分钟的 50 秒
        int second = (DateUtil.second(nowDate) / 10 - 1) * 10;
        if (second == -10) {
            second = 50;
            // 回到上一分钟
            nowDate = DateUtil.offsetMinute(nowDate, -1);
        }
        String date = DateUtil.format(nowDate, "HH:mm:") + second;
        syncThumb2DBByDate(date);
        log.info("临时数据同步完成");
    }

    /**
     * 同步数据
     * @param date 对应数据的时间片
     */
    public void syncThumb2DBByDate(String date) {
        // 获取到临时点赞和取消点赞数据
        String tempThumbKey = RedisKeyUtil.getTempThumbKey(date);   // 临时点赞表 thumb:temp:HH:mm:ss
        // （userId:blogId, 操作类型（1点赞、-1取消点赞、0未改变））
        Map<Object, Object> allTempThumbMap = redisTemplate.opsForHash().entries(tempThumbKey);
        boolean thumbMapEmpty = CollUtil.isEmpty(allTempThumbMap);
        if (thumbMapEmpty) {
            return;
        }
        // 同步 点赞 到数据库 构建插入列表并收集blogId
        Map<Long, Long> blogThumbCountMap = new HashMap<>();  // 博客 id : 点赞数
        ArrayList<Thumb> thumbList = new ArrayList<>();  // 用于保存插入数据
        LambdaQueryWrapper<Thumb> wrapper = new LambdaQueryWrapper<>();  // 用于构造删除数据

        boolean needRemove = false;
        for (Object userIdBlogIdObj : allTempThumbMap.keySet()) {  // 变量键值
            String userIdBlogId = (String) userIdBlogIdObj;  
            String[] userIdAndBlogId = userIdBlogId.split(StrPool.COLON);
            Long userId = Long.valueOf(userIdAndBlogId[0]);  // 用户 id
            Long blogId = Long.valueOf(userIdAndBlogId[1]);  // 博客 id
            // -1 取消点赞 1 点赞
            Integer thumbType = Integer.valueOf(allTempThumbMap.get(userIdBlogId).toString());  
            if (thumbType.equals(ThumbTypeEnum.INCR.getValue())) {  // 点赞  添加数据
                Thumb thumb = new Thumb();  
                thumb.setUserId(userId);
                // TODO 当前的博客ID可能不在数据库中
                thumb.setBlogId(blogId);  
                thumbList.add(thumb);  
            } else if (thumbType.equals(ThumbTypeEnum.DECR.getValue())) {  // 取消点赞  删除数据
                // 拼接查询条件 批量删除
                needRemove = true;
                wrapper.or().eq(Thumb::getUserId, userId).eq(Thumb::getBlogId, blogId);  
            } else {  // 无效数据
                if (!thumbType.equals(ThumbTypeEnum.NON.getValue())) {
                    log.warn("数据异常：{}", userId + "," + blogId + "," + thumbType);  
                }  
                continue;  
            }  

            // 计算点赞增量
            blogThumbCountMap.put(blogId, blogThumbCountMap.getOrDefault(blogId, 0L) + thumbType);
        }  
        // 批量插入  与真实的点赞时间相差不超过10s
        thumbService.saveBatch(thumbList);  
        // 批量删除  
        if (needRemove) {  
            thumbService.remove(wrapper);  
        }  
        // 批量更新博客点赞量  
        if (!blogThumbCountMap.isEmpty()) {  
            blogMapper.batchUpdateThumbCount(blogThumbCountMap);  
        }  
        // 异步 redis 中临时数据删除  Java21 虚拟线程
        Thread.startVirtualThread(() -> redisTemplate.delete(tempThumbKey));
    }  
}
